#include "modelingperspective.h"

#include "../documents/data/graphicdata.h"
#include "../editors/projectnavigator.h"
#include "../mainwindow.h"
#include "../modelvalidator/modelvalidatorview.h"
#include "../utils/documentmanager.h"
#include "../utils/modelvalidator.h"
#include "../utils/undomanager.h"

#include <coremimetypes.h>
#include <dockwidget.h>
#include <editorgallery.h>
#include <iconmanager.h>
#include <mimetypemanager.h>
#include <propertyeditor.h>

#include <QtCore/QSettings>
#include <QtGui/QAction>
#include <QtGui/QFrame>
#include <QtGui/QLabel>
#include <QtGui/QScrollArea>
#include <QtGui/QToolBar>
#include <QtGui/QUndoView>

using namespace core;
using namespace gui;
using namespace swp;

ModelingPerspective::ModelingPerspective(PerspectiveManager* manager,
    QWidget* parent) :
        QMainWindow(parent), IPerspective(),
        m_manager(0),
        m_projectNavigatorDock(0),
        m_galleryDock(0),
        m_historyDock(0),
        m_propertyEditorDock(0),
        m_validatorViewDock(0),
        m_projectNavigator(0),
        m_gallery(0),
        m_history(0),
        m_propertyEditor(0),
        m_validatorView(0),
        m_editorArea(0),
        m_projectToolbar(0),
        m_viewSeparator(0),
        m_newProjectMenuAction(0),
        m_openProjectMenuAction(0),
        m_closeProjectMenuAction(0),
        m_saveProjectMenuAction(0),
        m_importToProjectMenuAction(0),
        m_newProjectAction(0),
        m_openProjectAction(0),
        m_closeProjectAction(0),
        m_saveProjectAction(0),
        m_connectedToPropertyEditor()
{
    IconManager* iconManager = IconManager::getInstance();
    iconManager->registerStyleIcon("SwpNewProject",
        ":/swp/icons/new_project_icon.png");
    iconManager->registerStyleIcon("SwpCloseProject",
        ":/swp/icons/close_project_icon.png");
    iconManager->registerStyleIcon("SwpOpenProject",
        ":/swp/icons/open_project_icon.png");
    iconManager->registerStyleIcon("SwpSaveProject",
        ":/swp/icons/save_project_icon.png");
    iconManager->registerStyleIcon("SwpNewProjectMenu",
        ":/swp/icons/new_project_menu_icon.png");
    iconManager->registerStyleIcon("SwpCloseProjectMenu",
        ":/swp/icons/close_project_menu_icon.png");
    iconManager->registerStyleIcon("SwpOpenProjectMenu",
        ":/swp/icons/open_project_menu_icon.png");
    iconManager->registerStyleIcon("SwpSaveProjectMenu",
        ":/swp/icons/save_project_menu_icon.png");
    iconManager->registerStyleIcon("SwpImportToProjectMenu",
        ":/swp/icons/import_menu_icon.png");

    createActions();
    createCentralWidget();
    createDockWidgets();
    createToolBars();
    connectSlots();
}

ModelingPerspective::~ModelingPerspective()
{

}

QList<QAction* > ModelingPerspective::getStaticActions(
    const QString& menuId) const
{
    QList<QAction* > ret;

    if (menuId == PROJECT_MENU)
    {
        ret.append(m_newProjectMenuAction);
        QAction* sep1 = new QAction(MainWindow::getInstance());
        sep1->setSeparator(true);
        ret.append(sep1);
        ret.append(m_openProjectMenuAction);
        ret.append(m_saveProjectMenuAction);
        QAction* sep2 = new QAction(MainWindow::getInstance());
        sep2->setSeparator(true);
        ret.append(sep2);
        ret.append(m_closeProjectMenuAction);
        QAction* sep3 = new QAction(MainWindow::getInstance());
        sep3->setSeparator(true);
        ret.append(sep3);
        ret.append(m_importToProjectMenuAction);
    }
    else if (menuId == FINISHED)
    {
        m_projectNavigator->addWindowMenuActions();
    }

    return ret;
}

QList<QAction* > ModelingPerspective::getContextActions(
    const QString& menuId) const
{
    QList<QAction* > ret;

    if (menuId == VIEW_MENU)
    {
        ret.append(m_viewSeparator);
        ret.append(m_projectNavigatorDock->toggleViewAction());
        ret.append(m_galleryDock->toggleViewAction());
        ret.append(m_historyDock->toggleViewAction());
        ret.append(m_propertyEditorDock->toggleViewAction());
        ret.append(m_validatorViewDock->toggleViewAction());
    }

    return ret;
}

QList<QToolBar* > ModelingPerspective::getStaticToolBars() const
{
    QList<QToolBar* > ret;

    ret.append(m_projectToolbar);

    return ret;
}

QList<QToolBar* > ModelingPerspective::getContextToolBars() const
{
    QList<QToolBar* > ret;

    return ret;
}

QList<QAction* > ModelingPerspective::getPerspectiveEnsuringActions() const
{
    QList<QAction* > ret;

    ret.append(m_newProjectMenuAction);
    ret.append(m_openProjectMenuAction);
    ret.append(m_newProjectAction);
    ret.append(m_openProjectAction);
    ret.append(m_projectNavigator->perspectiveEnsuringActions());

    return ret;
}

IEditorArea* ModelingPerspective::getEditorArea() const
{
    return m_editorArea;
}

bool ModelingPerspective::canOpen(const MimeType& type) const
{
    bool ret =
        MimeTypeManager::getInstance()->mimeType(CLASS_DIAGRAM_MIMETYPE) ==
            type ||
        MimeTypeManager::getInstance()->mimeType(ACTIVITY_DIAGRAM_MIMETYPE) ==
            type;

    return ret;
}

void ModelingPerspective::handleIsCurrentChanged(bool current)
{
    if (current)
    {
        const bool enable =
            DocumentManager::getInstance()->openedProjects().size() > 0;
        m_closeProjectMenuAction->setEnabled(enable);
        m_closeProjectAction->setEnabled(enable);
        m_closeProjectAction->setVisible(true);

        UndoManager::getInstance()->bindView(m_history);
    }
    else
    {
        m_closeProjectMenuAction->setEnabled(false);
        m_closeProjectAction->setEnabled(false);
        m_closeProjectAction->setVisible(false);
    }
}

void ModelingPerspective::readSettings(QSettings* settings)
{
    const QStringList groups = settings->childGroups();

    if (groups.contains("ModelingPerspective"))
    {
        settings->beginGroup("ModelingPerspective");
        restoreState(settings->value("state").toByteArray());
        settings->endGroup();
    }
}

void ModelingPerspective::writeSettings(QSettings* settings) const
{
    settings->beginGroup("ModelingPerspective");
    settings->setValue("state", saveState());
    settings->endGroup();
}

ProjectNavigator* ModelingPerspective::projectNavigator() const
{
    return m_projectNavigator;
}

EditorGallery* ModelingPerspective::gallery() const
{
    return m_gallery;
}

PropertyEditor* ModelingPerspective::propertyEditor() const
{
    return m_propertyEditor;
}

void ModelingPerspective::createActions()
{
    m_viewSeparator = new QAction(this);
    m_viewSeparator->setSeparator(true);

    IconManager* iconManager = IconManager::getInstance();

    m_newProjectAction = new QAction(tr("&New"), this);
    if (iconManager->hasIcon("SwpNewProject"))
    {
        m_newProjectAction->setIcon(iconManager->icon(
            "SwpNewProject"));
    }

    m_openProjectAction = new QAction(tr("&Open"), this);
    if (iconManager->hasIcon("SwpOpenProject"))
    {
        m_openProjectAction->setIcon(iconManager->icon(
            "SwpOpenProject"));
    }

    m_saveProjectAction = new QAction(tr("&Save"), this);
    m_saveProjectAction->setEnabled(false);
    if (iconManager->hasIcon("SwpSaveProject"))
    {
        m_saveProjectAction->setIcon(iconManager->icon(
            "SwpSaveProject"));
    }

    m_closeProjectAction = new QAction(tr("&Close"), this);
    m_closeProjectAction->setEnabled(false);
    if (iconManager->hasIcon("SwpCloseProject"))
    {
        m_closeProjectAction->setIcon(iconManager->icon(
            "SwpCloseProject"));
    }

    m_newProjectMenuAction = new QAction(tr("&New"), this);
    m_newProjectMenuAction->setShortcut(QKeySequence(
        Qt::CTRL + Qt::SHIFT + Qt::Key_N));
    m_newProjectMenuAction->setShortcutContext(Qt::WindowShortcut);
    if (iconManager->hasIcon("SwpNewProjectMenu"))
    {
        m_newProjectMenuAction->setIcon(iconManager->icon(
            "SwpNewProjectMenu"));
    }

    m_openProjectMenuAction = new QAction(tr("&Open"), this);
    m_openProjectMenuAction->setShortcuts(QKeySequence::Open);
    m_openProjectMenuAction->setShortcutContext(Qt::WindowShortcut);
    if (iconManager->hasIcon("SwpOpenProjectMenu"))
    {
        m_openProjectMenuAction->setIcon(iconManager->icon(
            "SwpOpenProjectMenu"));
    }

    m_saveProjectMenuAction = new QAction(tr("&Save"), this);
    m_saveProjectMenuAction->setShortcuts(QKeySequence::Save);
    m_saveProjectMenuAction->setShortcutContext(Qt::WindowShortcut);
    m_saveProjectMenuAction->setEnabled(false);
    if (iconManager->hasIcon("SwpSaveProjectMenu"))
    {
        m_saveProjectMenuAction->setIcon(iconManager->icon(
            "SwpSaveProjectMenu"));
    }

    m_closeProjectMenuAction = new QAction(tr("&Close"), this);
    m_closeProjectMenuAction->setShortcuts(QKeySequence::Close);
    m_closeProjectMenuAction->setShortcutContext(Qt::WindowShortcut);
    m_closeProjectMenuAction->setEnabled(false);
    if (iconManager->hasIcon("SwpCloseProjectMenu"))
    {
        m_closeProjectMenuAction->setIcon(iconManager->icon(
            "SwpCloseProjectMenu"));
    }

    m_importToProjectMenuAction = new QAction(tr("&Import"), this);
    m_importToProjectMenuAction->setEnabled(false);
    if (iconManager->hasIcon("SwpImportToProjectMenu"))
    {
        m_importToProjectMenuAction->setIcon(iconManager->icon(
            "SwpImportToProjectMenu"));
    }
}

void ModelingPerspective::createCentralWidget()
{
    m_editorArea = new EditorArea(this);

    setCentralWidget(m_editorArea);
}

void ModelingPerspective::createDockWidgets()
{
    QList<QAction* > projectDependent;
    projectDependent << m_saveProjectMenuAction << m_saveProjectAction <<
        m_closeProjectMenuAction << m_closeProjectAction <<
        m_importToProjectMenuAction;
    m_projectNavigator = new ProjectNavigator(projectDependent);

    m_gallery = new EditorGallery();

    m_history = new QUndoView();
    m_history->setAlternatingRowColors(true);

    m_propertyEditor = new PropertyEditor();

    m_validatorView = new ModelValidatorView();

    m_projectNavigatorDock = new DockWidget(tr("Project Navigator"), this);
    m_projectNavigatorDock->setObjectName("ProjectNavigatorDockWidget");
    m_projectNavigatorDock->setWidget(m_projectNavigator);
    m_projectNavigatorDock->setAllowedAreas(Qt::LeftDockWidgetArea |
        Qt::RightDockWidgetArea);
    m_projectNavigatorDock->setFeatures(
        QDockWidget::DockWidgetClosable |
        QDockWidget::DockWidgetMovable);
    m_projectNavigatorDock->setMinimumWidth(150);

    m_galleryDock = new DockWidget(tr("Component gallery"), this);
    m_galleryDock->setObjectName("GalleryDockWidget");
    m_galleryDock->setWidget(m_gallery);
    m_galleryDock->setAllowedAreas(Qt::LeftDockWidgetArea |
        Qt::RightDockWidgetArea);
    m_galleryDock->setFeatures(
        QDockWidget::DockWidgetClosable |
        QDockWidget::DockWidgetMovable);
    m_galleryDock->setMinimumWidth(130);

    m_historyDock = new DockWidget(tr("Document History"), this);
    m_historyDock->setObjectName("HistoryDockWidget");
    m_historyDock->setWidget(m_history);
    m_historyDock->setFeatures(
        QDockWidget::DockWidgetClosable |
        QDockWidget::DockWidgetMovable);
    m_historyDock->setMinimumWidth(130);

    m_propertyEditorDock = new DockWidget(tr("Properties"), this);
    m_propertyEditorDock->setObjectName("PropertiesDockWidget");
    m_propertyEditorDock->setWidget(m_propertyEditor);
    m_propertyEditorDock->setFeatures(
        QDockWidget::DockWidgetClosable |
        QDockWidget::DockWidgetMovable);
    m_propertyEditorDock->setMinimumWidth(150);

    m_validatorViewDock = new DockWidget(tr("Validator output"), this);
    m_validatorViewDock->setObjectName("ValidatorDockWidget");
    m_validatorViewDock->setWidget(m_validatorView);
    m_validatorViewDock->setFeatures(QDockWidget::DockWidgetClosable);

    addDockWidget(Qt::LeftDockWidgetArea, m_projectNavigatorDock);
    addDockWidget(Qt::RightDockWidgetArea, m_galleryDock);
    addDockWidget(Qt::RightDockWidgetArea, m_historyDock);
    addDockWidget(Qt::LeftDockWidgetArea, m_propertyEditorDock);
    addDockWidget(Qt::BottomDockWidgetArea, m_validatorViewDock);

    m_validatorViewDock->hide();
}

void ModelingPerspective::createToolBars()
{
    m_projectToolbar = new QToolBar(tr("Project"));
    m_projectToolbar->setObjectName("ProjectToolbar");
    m_projectToolbar->addAction(m_newProjectAction);
    m_projectToolbar->addAction(m_openProjectAction);
    m_projectToolbar->addAction(m_saveProjectAction);
    m_projectToolbar->addAction(m_closeProjectAction);

    m_projectNavigator->addToolBarActions(m_projectToolbar);
}

void ModelingPerspective::connectSlots()
{
    DocumentManager* dm = DocumentManager::getInstance();

    connect(m_newProjectAction, SIGNAL(triggered()),
        dm, SLOT(newProject()));

    connect(m_openProjectAction, SIGNAL(triggered()),
        dm, SLOT(openProject()));

    connect(m_saveProjectAction, SIGNAL(triggered()),
        MainWindow::getInstance(), SLOT(sendSaveEvent()));

    connect(m_closeProjectAction, SIGNAL(triggered()),
        m_projectNavigator,
        SLOT(closeSelectedProject()));

    connect(m_newProjectMenuAction, SIGNAL(triggered()),
        dm, SLOT(newProject()));

    connect(m_openProjectMenuAction, SIGNAL(triggered()),
        dm, SLOT(openProject()));

    connect(m_saveProjectMenuAction, SIGNAL(triggered()),
        MainWindow::getInstance(), SLOT(sendSaveEvent()));

    connect(m_closeProjectMenuAction, SIGNAL(triggered()),
        m_projectNavigator,
        SLOT(closeSelectedProject()));

    connect(m_importToProjectMenuAction, SIGNAL(triggered()),
        m_projectNavigator,
        SLOT(importToSelectedProject()));

    connect(dm, SIGNAL(projectCreated(
        QSharedPointer<core::IDocument>)),
            m_projectNavigator, SLOT(open(
                QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(projectOpened(
        QSharedPointer<core::IDocument>)),
            m_projectNavigator, SLOT(open(
                QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentClosed(
        QSharedPointer<core::IDocument>)),
            m_projectNavigator, SLOT(close(
                QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentAdded(
        QSharedPointer<core::IDocument>, QSharedPointer<core::IDocument>)),
            m_projectNavigator, SLOT(add(
                QSharedPointer<core::IDocument>,
                QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentRemoved(
        QSharedPointer<core::IDocument>, QSharedPointer<core::IDocument>)),
            m_projectNavigator, SLOT(remove(
                QSharedPointer<core::IDocument>,
                QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentRemoved(
        QSharedPointer<core::IDocument>)),
            m_projectNavigator, SLOT(remove(
                QSharedPointer<core::IDocument>)));

    connect(m_projectNavigator, SIGNAL(aboutToOpen(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(openDocument(
                QSharedPointer<core::IDocument>)));
    connect(m_projectNavigator, SIGNAL(aboutToSave(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(saveDocument(
                QSharedPointer<core::IDocument>)));
    connect(m_projectNavigator, SIGNAL(aboutToClose(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(closeDocument(
                QSharedPointer<core::IDocument>)));
    connect(m_projectNavigator, SIGNAL(aboutToAdd(
        QSharedPointer<core::IDocument>, QList<core::MimeType>, uint)),
            dm, SLOT(addDocument(
                QSharedPointer<core::IDocument>, QList<core::MimeType>, uint)));
    connect(m_projectNavigator, SIGNAL(aboutToRemove(
        QSharedPointer<core::IDocument>, QSharedPointer<core::IDocument>)),
            dm, SLOT(removeDocument(
                QSharedPointer<core::IDocument>,
                QSharedPointer<core::IDocument>)));
    connect(m_projectNavigator, SIGNAL(aboutToDelete(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(deleteDocument(
                QSharedPointer<core::IDocument>)));
    connect(m_projectNavigator, SIGNAL(aboutToMove(
        QSharedPointer<core::IDocument>, QSharedPointer<core::IDocument>)),
            dm, SLOT(moveDocument(
                QSharedPointer<core::IDocument>,
                    QSharedPointer<core::IDocument>)));

    connect(m_editorArea, SIGNAL(editorOpened(QSharedPointer<core::IEditor>)),
        this, SLOT(connectPropertyEditor(
            QSharedPointer<core::IEditor>)));
    connect(dm, SIGNAL(documentClosed(QSharedPointer<core::IDocument>)),
        this, SLOT(disconnectPropertyEditor(
            QSharedPointer<core::IDocument>)));
    connect(m_editorArea,
        SIGNAL(currentChanged(QSharedPointer<core::IEditor>)),
        this, SLOT(resetPropertyEditor(QSharedPointer<core::IEditor>)));

    connect(ModelValidator::getInstance(), SIGNAL(validatingFinished()),
        m_validatorViewDock, SLOT(show()));
    connect(ModelValidator::getInstance(), SIGNAL(validatingFinished()),
        this, SLOT(expandReport()));

    connect(ModelValidator::getInstance(),
        SIGNAL(selectProjectRequest(QSharedPointer<core::IContainerDocument>)),
        m_projectNavigator,
        SLOT(tryToSelectProject(QSharedPointer<core::IContainerDocument>)));
    connect(ModelValidator::getInstance(),
        SIGNAL(highlightDocumentRequest(QSharedPointer<core::IDocument>)),
        this,
        SLOT(highlightDocument(QSharedPointer<core::IDocument>)));

    MainWindow* win = MainWindow::getInstance();

    connect(m_editorArea, SIGNAL(
        currentChanged(QSharedPointer<core::IEditor>)),
            win, SLOT(
                handleEditorChange(QSharedPointer<core::IEditor>)));
    connect(m_editorArea, SIGNAL(
        aboutToClose(QSharedPointer<core::IEditor>)),
            dm, SLOT(
                closeDocument(QSharedPointer<core::IEditor>)));
    connect(dm, SIGNAL(
        documentClosed(QSharedPointer<core::IEditor>)),
            m_editorArea, SLOT(
                confirmClose(QSharedPointer<core::IEditor>)));
    connect(m_editorArea, SIGNAL(aboutToSave(
        QSharedPointer<core::IDocument>)), dm, SLOT(
            saveDocument(QSharedPointer<core::IDocument>)));

    connect(m_editorArea, SIGNAL(
        currentChanged(QSharedPointer<core::IEditor>)),
            m_gallery, SLOT(
                refreshGallery(QSharedPointer<core::IEditor>)));

    win->registerForSaveNotification(this, m_editorArea);
    win->registerForSaveNotification(this, m_projectNavigator);
    win->registerForCloseNotification(this, m_projectNavigator);
}

void ModelingPerspective::connectPropertyEditor(
    const QSharedPointer<core::IEditor>& editor)
{
    if (editor->document()->data()->type() == IDocumentData::GraphicModel)
    {
        GraphicData* data = dynamic_cast<GraphicData*>(
            editor->document()->data());

        Q_ASSERT(data != 0); //bad data type

        connect(data, SIGNAL(editorItemSelectionChanged(
            core::IGraphicEditorItem*)),
                this, SLOT(notifyPropertyEditor(core::IGraphicEditorItem*)));

        m_connectedToPropertyEditor.append(data);
    }
}

void ModelingPerspective::disconnectPropertyEditor(
    const QSharedPointer<core::IDocument>& document)
{
    if (document->data()->type() == IDocumentData::GraphicModel)
    {
        GraphicData* data = dynamic_cast<GraphicData*>(document->data());

        Q_ASSERT(data != 0); //bad data type

        if (m_connectedToPropertyEditor.contains(data))
        {
            disconnect(data, SIGNAL(editorItemSelectionChanged(
                core::IGraphicEditorItem*)),
                    this, SLOT(notifyPropertyEditor(
                        core::IGraphicEditorItem*)));

            m_propertyEditor->propertyObjectChanged(0);

            m_connectedToPropertyEditor.removeAll(data);
        }
    }
}

void ModelingPerspective::notifyPropertyEditor(core::IGraphicEditorItem* item)
{
    m_propertyEditor->propertyObjectChanged(item);
}

void ModelingPerspective::resetPropertyEditor(
    const QSharedPointer<IEditor>& editor)
{
    if (!editor.isNull())
    {
        const QSharedPointer<IDocument> document = editor->document();

        if (document->data()->type() == IDocumentData::GraphicModel)
        {
            GraphicData* data = dynamic_cast<GraphicData*>(document->data());

            Q_ASSERT(data != 0); //bad data type

            const QList<IGraphicEditorItem* > selected =
                data->selectedGraphicItems();

            if (selected.size() > 0)
            {
                m_propertyEditor->propertyObjectChanged(selected.last());
            }
            else
            {
                m_propertyEditor->propertyObjectChanged(0);
            }
        }
        else
        {
            m_propertyEditor->propertyObjectChanged(0);
        }
    }
    else
    {
        m_propertyEditor->propertyObjectChanged(0);
    }
}

void ModelingPerspective::expandReport()
{
    m_validatorView->expand(m_validatorView->model()->index(0, 0));
}

void ModelingPerspective::highlightDocument(
    const QSharedPointer<IDocument>& document)
{
    m_projectNavigator->tryToSelectDocument(document);

    if (!document->isFuture() && canOpen(document->mimeType()))
    {
        m_editorArea->openEditor(document);
    }
}
